iT邦幫忙

2025 iThome 鐵人賽

DAY 2
0
Vue.js

需求至上的 Vue 魔法之旅系列 第 2

Day 1:從「這不是我要的」開始:流程圖推導到 v-if 與 :disabled

  • 分享至 

  • xImage
  •  

前言|系列架構與 Chapter說明

這一系列文章的主軸是 「需求至上」的 Vue 3 學習之旅。
我會預設大家都是有JS開發經驗的人~
本篇章主要是把每個實戰按理當作說書的方式來執行

我不會逐條講解 API,而是用 「需求 → user story/data schema/ flow → 對應的 Vue 功能」 的方式,讓每一篇文章都從真實需求開始,經過邏輯推導與系統設計,最後自然帶出 Vue 的寫法與觀念。

整個系列分成多個 Chapter
每一個 Chapter 就像一個小型專案或情境任務。

第一個 Chapter 是 「點飲料系統」:我們會從一杯飲料的最小需求出發,一步步加入事件處理、輸入驗證、樣式綁定、訂單統計,並同時建立對 ** v-if、v-model、@click、:class、reactive** 等 Vue 核心能力的深刻理解。

Chapter 1:點飲料系統 – 以需求推導 Vue 功能

每個day內容焦點

  • Day 1:從「這不是我要的」開始:流程圖推導到 v-if 與 :disabled
  • Day 2:點餐按鈕啟動:@click 把點單動作寫進系統
  • Day 3:輸入姓名與備註:v-model 打通點單雙向資料流
  • Day 4:狀態驅動外觀:用 :class 高亮已完成的飲料訂單
  • Day 5:建立飲料清單:用 reactive 陣列統計每杯客製化
Day 標題 核心學習內容
Day 1 從「這不是我要的」開始:流程圖推導到 v-if:disabled 只處理「紅茶/綠茶」與「甜度、冰量」的基本判定。學會 v-if:disabled,並用需求驅動狀態 refonchange
Day 2 事件觸發的本質:為什麼需要 @click 才能把使用者行為轉成狀態 加入「送出」事件。理解 @click 與方法綁定,完成點餐資料的第一次儲存。
Day 3 輸入框的需求:v-model 背後的雙向資料流思維 引入使用者姓名與備註輸入框,深入理解 v-model 與雙向資料流。
Day 4 為什麼需要資料綁定?用「狀態→樣式」引出 :class 根據訂單狀態自動切換樣式,例如完成下單後改變背景或標籤顏色。
Day 5 系統需求:清單管理 → reactive 陣列 → 新增/刪除/統計 管理多筆飲料訂單,用 reactive 陣列彙總、刪除、統計每種客製化飲品的數量。

** 在 Part 1 結束時,我們會擁有一個能 完整輸入、送出並統計飲料訂單 的前端小系統。 **

https://ithelp.ithome.com.tw/upload/images/20250916/20121052P2P7nuMuqR.png

一、需求說明

我需要一個網頁表單可以讓使用者填寫想喝的飲料、客製化(糖/冰),統計完後統計者(秘書)可以自動計算「飲料 + 客製化」的每種組合數量。
今日僅做選項判定與摘要顯示;統計會在 Part 1 後續章節完成。

規格限制(今日版)

飲料:紅茶、綠茶

甜度:正常甜、去糖

冰量:正常冰、去冰

今天的目標大概是這樣的進度

時序圖

https://ithelp.ithome.com.tw/upload/images/20250916/201210527AeN6wtUob.png

程式的流程控制

https://ithelp.ithome.com.tw/upload/images/20250916/20121052idSuaPBaos.png

我的今天的流程控制
會先一關一關選

  1. 先選飲料,選完後會通知user顯示選擇甜度
  2. 甜度選擇完後,你身上會有飲料跟甜度的data
  3. 最後選完冰量就可以送出單子(今天還沒交click我都用顯示的方式直接顯示點選的清單)
  4. 最後下方會顯示整個照片的memo

例如選惹飲料沒選甜度的UI會長這樣
https://ithelp.ithome.com.tw/upload/images/20250916/20121052arNRqXsFQZ.png

都選完了會長這樣
https://ithelp.ithome.com.tw/upload/images/20250916/20121052715xuoOR37.png

二、程式動工

我們會先把整個vue的框架架構簡單講解出來

首先我們採用的是VUE SFC的架構(以後component架構再講,先操表就對了!!)

2.1 Template 區塊

vue有一個最基礎的型態它裡面可以寫

  1. html的tag
  2. vue的語法糖(各種事件跟綁定屬性的功用)

<template>

</template>

HTML TAG

像 <h2>、<fieldset>、<label>、<input>、<button>  

Vue 語法糖:

  1. 指令(Directives):v-if、v-else、v-if="drink && sweetness"
    → 根據條件決定元素要不要顯示。
  • 判斷條件:drink && sweetness

  • 在 JavaScript 中,「字串」如果不是空字串就算 true。

  • A && B 代表:A 與 B 都必須為真。

  • 作用:只有當 drink 和 sweetness 都有值 時,才會渲染這個 。

  • 流程上的意思:

  • 使用者 至少選了飲料 → 甜度選項出現。

  • 使用者 同時選了飲料和甜度 → 冰量選項才出現。

  1. 屬性綁定::disabled="!canSubmit"
    → : 是 v-bind 的簡寫,把 JS 的布林值綁到 HTML 屬性。

  2. 事件綁定:@change="onDrinkChange('紅茶')"
    → @ 是 v-on 的簡寫,用來綁定事件並呼叫方法。

  • @change="onDrinkChange('紅茶')"

用途:監聽元素的 change 事件。

  • @ 是 v-on 的縮寫,表示「監聽事件」。

change 是瀏覽器的標準事件,表示輸入內容改變。

  • "onDrinkChange('紅茶')" 代表事件觸發時執行方法 onDrinkChange,並把 '紅茶' 傳入。

在 script setup 內,我們用:

  1. 插值語法:{{ drink }}
    → 顯示對應的 JavaScript 變數值。

  2. computed(() => drink.value && sweetness.value && ice.value)

  • 這是一個根據其他狀態自動運算的值。

  • 只有當 drink、sweetness、ice 都有值時,這個運算式才為 true。

  • 可以把它例解釋自動判定的計算機

2.2 script 區塊

<script setup>
import { ref, computed } from 'vue'

const drink = ref('')
const sweetness = ref('')
const ice = ref('')

function onDrinkChange(value) { drink.value = value }
function onSweetnessChange(value) { sweetness.value = value }
function onIceChange(value) { ice.value = value }

const canSubmit = computed(() => drink.value && sweetness.value && ice.value)
</script>

用途:寫所有與邏輯、狀態、函式相關的 JavaScript 程式碼。

特點:

使用 setup 語法糖,不需要 export default 或 data()、methods,程式更精簡。

script setup 內宣告的變數與方法,可以直接在 template 中使用。

程式重點:

  1. ref(''):宣告可以響應的變數(drink、sweetness、ice),初始值為空字串。

  2. 三個 onXXXChange():接收使用者點選的值並更新狀態。

  3. computed:自動計算出 canSubmit,只有三個選項都選了才會是 true。

3.完成程式碼

<template>
  <h2>飲料點單(Day 1)</h2>

  <!-- 步驟 1:飲料 -->
  <fieldset>
    <legend>步驟 1:選擇飲料</legend>
    <label>
      <input type="radio" name="drink" value="紅茶" @change="onDrinkChange('紅茶')"> 紅茶
    </label>
    <label>
      <input type="radio" name="drink" value="綠茶" @change="onDrinkChange('綠茶')"> 綠茶
    </label>
    <p v-if="!drink">⚠️ 尚未選取飲料</p>
    <p v-else>✅ 已選:{{ drink }}</p>
  </fieldset>

  <!-- 步驟 2:甜度(只在選好飲料後顯示) -->
  <fieldset v-if="drink">
    <legend>步驟 2:選擇甜度</legend>
    <label>
      <input type="radio" name="sweetness" value="正常甜" @change="onSweetnessChange('正常甜')"> 正常甜
    </label>
    <label>
      <input type="radio" name="sweetness" value="去糖" @change="onSweetnessChange('去糖')"> 去糖
    </label>
    <p v-if="!sweetness">⚠️ 尚未選擇甜度</p>
    <p v-else>✅ 已選:{{ sweetness }}</p>
  </fieldset>

  <!-- 步驟 3:冰量(只在選好甜度後顯示) -->
  <fieldset v-if="drink && sweetness">
    <legend>步驟 3:選擇冰量</legend>
    <label>
      <input type="radio" name="ice" value="正常冰" @change="onIceChange('正常冰')"> 正常冰
    </label>
    <label>
      <input type="radio" name="ice" value="去冰" @change="onIceChange('去冰')"> 去冰
    </label>
    <p v-if="!ice">⚠️ 尚未選擇冰量</p>
    <p v-else>✅ 已選:{{ ice }}</p>
  </fieldset>

  <!-- 送出按鈕與摘要 -->
  <button :disabled="!canSubmit">送出</button>
  <p v-if="canSubmit">
    你的選擇:{{ drink }} / {{ ice }} / {{ sweetness }}
  </p>
</template>

<script setup>
import { ref, computed } from 'vue'

// 儲存使用者選擇
const drink = ref('')
const sweetness = ref('')
const ice = ref('')

// 逐步事件處理
function onDrinkChange(value) {
  drink.value = value
}
function onSweetnessChange(value) {
  sweetness.value = value
}
function onIceChange(value) {
  ice.value = value
}

// 全部完成才能送出
const canSubmit = computed(() => drink.value && sweetness.value && ice.value)
</script>

飲料菜單點餐D1

總結

完成後我們就可以驗證是否完成!!
我們可以根據使用者情境一關一關點餐完成飲料的系統

明天再繼續完成這個系統吧!!!


上一篇
Day 0|起心動念與系列規劃
下一篇
Day 2 : 事件觸發的本質:為什麼需要 @click 才能把使用者行為轉成狀態
系列文
需求至上的 Vue 魔法之旅3
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言